﻿using Cinch;
using FR.SAFTAnalyser.Model;
using FR.SAFTAnalyser.SAFT;
using FR.SAFTAnalyser.ViewModels.Pane;
using MEFedMVVM.Common;
using OfficeOpenXml;
using OfficeOpenXml.Table;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Controls;

namespace FR.SAFTAnalyser.ViewModels
{
    public class InvoicesViewModel : DocumentPartViewModel
    {
        public InvoicesViewModel()
            : base("Documentos comerciais")
        {
            if (!Designer.IsInDesignMode)
            {
                Mediator.Instance.Register(this);

                DoOpenExcelCommand = new SimpleCommand<object, object>(CanOpenExcel, OpenExcel);
                ShowInvoiceDetailsCommand = new SimpleCommand<object, object>(CanShowInvoiceDetails, DoShowInvoiceDetails);
                ShowCustomerCommand = new SimpleCommand<object, object>(CanShowCustomer, DoShowCustomer);
                TestHashCommand = new SimpleCommand<object, object>(CanTestHash, DoTestHash);

                this.filtroDataInicio = DateTime.MinValue;
                this.FiltroDataFim = DateTime.MaxValue;
            }
        }

        internal override void ResetSaftFile()
        {
            this.SalesInvoices = null;
        }

        #region Mediator messages
        [MediatorMessageSink(MessageType.SAFT_FILE_OPENED)]
        private void LoadInvoices(AuditFile saft)
        {
            if (saft != null && saft.SourceDocuments != null)
                this.SalesInvoices = saft.SourceDocuments.SalesInvoices;
        }

        [MediatorMessageSink(MessageType.INVOICE_LIST_CHANGED)]
        private void ReloadInvoices(AuditFile saft)
        {
            this.SalesInvoices = saft.SourceDocuments.SalesInvoices;
        }

        [MediatorMessageSink(MessageType.FOCUS_INVOICE)]
        private void FocusInvoice(string pk)
        {
            if (!string.IsNullOrEmpty(pk) && Invoices != null)
            {
                var invoicesWithError = from i in Invoices
                                        where i.Pk.AreEqualIgnoreCase(pk)
                                        select i;

                if (invoicesWithError != null)
                    CurrentInvoice = invoicesWithError.FirstOrDefault();
            }
        }

        [MediatorMessageSink(MessageType.FOCUS_INVOICE_LINE)]
        private void FocusInvoiceLine(string pk)
        {
            if (!string.IsNullOrEmpty(pk) && Invoices != null)
            {
                string[] pks = pk.Split(';');

                if (pks != null && pks.Length == 2)
                {
                    var invoicesWithError = from i in Invoices
                                            where i.Pk.AreEqualIgnoreCase(pks[1])
                                            select i;

                    if (invoicesWithError != null)
                    {
                        CurrentInvoice = invoicesWithError.FirstOrDefault();

                        if (currentInvoice != null)
                        {
                            var invoiceLineWithError = from l in CurrentInvoice.Line
                                                       where l.Pk.AreEqualIgnoreCase(pks[0])
                                                       select l;

                            if (invoiceLineWithError != null)
                                CurrentInvoiceLine = invoiceLineWithError.FirstOrDefault();

                        }
                    }
                }
            }
        }
        #endregion

        #region Binding properties
        SourceDocumentsSalesInvoices salesInvoices;
        public SourceDocumentsSalesInvoices SalesInvoices
        {
            get
            {
                if (salesInvoices == null && SAFT.SaftAnalyser.Instance.SaftFile != null)
                    salesInvoices = SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices;

                return salesInvoices;
            }
            set
            {
                salesInvoices = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.SalesInvoices));

                if (salesInvoices != null && string.IsNullOrEmpty(this.FilterInvoices))
                {
                    DocNumberOfEntries = salesInvoices.Invoice.Length;
                    DocTotalCredit = salesInvoices.Invoice
                                             .Where(c => c.DocumentStatus.InvoiceStatus != InvoiceStatus.A && c.DocumentStatus.InvoiceStatus != InvoiceStatus.F)
                                             .Sum(c => c.Line.Sum(l => l.ItemElementName == ItemChoiceType5.CreditAmount ? l.Item : 0))
                                             .ToString("c");

                    DocTotalDebit = salesInvoices.Invoice
                                            .Where(c => c.DocumentStatus.InvoiceStatus != InvoiceStatus.A && c.DocumentStatus.InvoiceStatus != InvoiceStatus.F)
                                            .Sum(c => c.Line.Sum(l => l.ItemElementName == ItemChoiceType5.DebitAmount ? l.Item : 0))
                                            .ToString("c");

                    NumberOfEntries = salesInvoices.NumberOfEntries;
                    TotalCredit = salesInvoices.TotalCredit.ToString("c");
                    TotalDebit = salesInvoices.TotalDebit.ToString("c");

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocNumberOfEntries));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocTotalCredit));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocTotalDebit));

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.NumberOfEntries));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.TotalCredit));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.TotalDebit));

                    this.filtroDataInicio = salesInvoices.Invoice.Min(i => i.InvoiceDate);
                    this.filtroDataFim = salesInvoices.Invoice.Max(i => i.InvoiceDate);

                    this.Invoices = salesInvoices.Invoice;
                }
            }
        }

        SourceDocumentsSalesInvoicesInvoice[] invoices;
        public SourceDocumentsSalesInvoicesInvoice[] Invoices
        {
            get { return invoices; }
            set
            {
                invoices = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.Invoices));
            }
        }

        SourceDocumentsSalesInvoicesInvoice currentInvoice;
        public SourceDocumentsSalesInvoicesInvoice CurrentInvoice
        {
            get { return currentInvoice; }
            set
            {
                currentInvoice = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.CurrentInvoice));

                if (currentInvoice != null && string.IsNullOrEmpty(this.FilterLines))
                {
                    DocGrossTotal = currentInvoice.Line.Sum(c => c.UnitPrice * (1 + c.Tax.Item * 0.01m) * c.Quantity * Operation(currentInvoice, c)).ToString("c");
                    DocNetTotal = currentInvoice.Line.Sum(c => c.UnitPrice * c.Quantity * Operation(currentInvoice, c)).ToString("c");
                    DocTaxPayable = currentInvoice.Line.Sum(c => c.UnitPrice * c.Tax.Item * 0.01m * c.Quantity * Operation(currentInvoice, c)).ToString("c");

                    GrossTotal = currentInvoice.DocumentTotals.GrossTotal.ToString("c");
                    NetTotal = currentInvoice.DocumentTotals.NetTotal.ToString("c");
                    TaxPayable = currentInvoice.DocumentTotals.TaxPayable.ToString("c");

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocGrossTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocNetTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocTaxPayable));

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.GrossTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.NetTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.TaxPayable));

                    if (!this.ShowAllLines)
                        this.CurrentInvoiceLines = currentInvoice.Line;
                }
            }
        }

        SourceDocumentsSalesInvoicesInvoiceLine[] currentInvoiceLines;
        public SourceDocumentsSalesInvoicesInvoiceLine[] CurrentInvoiceLines
        {
            get { return currentInvoiceLines; }
            set
            {
                currentInvoiceLines = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.CurrentInvoiceLines));
            }
        }

        SourceDocumentsSalesInvoicesInvoiceLine currentInvoiceLine;
        public SourceDocumentsSalesInvoicesInvoiceLine CurrentInvoiceLine
        {
            get { return currentInvoiceLine; }
            set
            {
                currentInvoiceLine = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.CurrentInvoiceLine));
            }
        }

        bool showAllLines;
        public bool ShowAllLines
        {
            get { return showAllLines; }
            set
            {
                showAllLines = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.ShowAllLines));

                if (showAllLines)
                    this.CurrentInvoiceLines = SalesInvoices.Invoice.SelectMany(i => i.Line).ToArray();
                else
                    this.CurrentInvoiceLines = CurrentInvoice.Line;
            }
        }

        DateTime filtroDataInicio;
        public DateTime FiltroDataInicio
        {
            get { return filtroDataInicio; }
            set
            {
                filtroDataInicio = value;
                FilterInvoicesByDate();
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.FiltroDataInicio));
            }
        }
        DateTime filtroDataFim;
        public DateTime FiltroDataFim
        {
            get { return filtroDataFim; }
            set
            {
                filtroDataFim = value;
                FilterInvoicesByDate();
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.FiltroDataFim));
            }
        }

        private void FilterInvoicesByDate()
        {
            if (SAFT.SaftAnalyser.Instance.SaftFile != null && SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices != null)
            {
                var inv = from i in SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices.Invoice
                          where i.InvoiceDate >= filtroDataInicio && i.InvoiceDate <= FiltroDataFim
                          select i;

                this.Invoices = inv.ToArray();
            }
        }

        string filterInvoices;
        public string FilterInvoices
        {
            get { return filterInvoices; }
            set
            {
                filterInvoices = value;

                if (string.IsNullOrEmpty(filterInvoices))
                    this.Invoices = SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices.Invoice;
                else
                {
                    var inv = from i in SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices.Invoice
                              where i.InvoiceNo.IndexOf(filterInvoices, StringComparison.OrdinalIgnoreCase) >= 0 ||
                              i.CustomerID.IndexOf(filterInvoices, StringComparison.OrdinalIgnoreCase) >= 0
                              select i;

                    this.Invoices = inv.ToArray();
                }
            }
        }

        string filterLines;
        public string FilterLines
        {
            get { return filterLines; }
            set
            {
                filterLines = value;

                if (string.IsNullOrEmpty(filterLines))
                    this.CurrentInvoiceLines = this.CurrentInvoice.Line;
                else
                {
                    var lines = from l in this.CurrentInvoice.Line
                                where l.ProductDescription.IndexOf(filterLines, StringComparison.OrdinalIgnoreCase) >= 0 ||
                                        l.ProductCode.IndexOf(filterLines, StringComparison.OrdinalIgnoreCase) >= 0 ||
                                        l.Description.IndexOf(filterLines, StringComparison.OrdinalIgnoreCase) >= 0
                                select l;

                    this.CurrentInvoiceLines = lines.ToArray();
                }
            }
        }

        public string DocGrossTotal { get; set; }

        public string DocNetTotal { get; set; }

        public string DocTaxPayable { get; set; }

        public int DocNumberOfEntries { get; set; }

        public string DocTotalCredit { get; set; }

        public string DocTotalDebit { get; set; }


        public string GrossTotal { get; set; }

        public string NetTotal { get; set; }

        public string TaxPayable { get; set; }

        public string NumberOfEntries { get; set; }

        public string TotalCredit { get; set; }

        public string TotalDebit { get; set; }
        #endregion

        #region Commands
        public SimpleCommand<object, object> TestHashCommand { get; set; }
        private void DoTestHash(object obj)
        {
            string hashDocumentoAnterior = null;

            //fazer parse do campo InvoiceNo, descobrir o nº do documento actual e obter o documento anterior
            string[] invoiceNo = this.CurrentInvoice.InvoiceNo.Split('/');
            if (invoiceNo != null && invoiceNo.Length == 2)
            {
                int num = 0;
                Int32.TryParse(invoiceNo[1], out num);
                num = num - 1;

                if (num > 0)
                {
                    SourceDocumentsSalesInvoicesInvoice invoice
                        = this.Invoices.Where(i => i.InvoiceNo.IndexOf(string.Format("{0}/{1}", invoiceNo[0], num), StringComparison.OrdinalIgnoreCase) == 0).FirstOrDefault();

                    //encontramos um documento, vamos obter a hash
                    if (invoice != null)
                        hashDocumentoAnterior = invoice.Hash;
                }
            }

            Workspace.Workspace.Instance.ShowCreateHashTool(this.CurrentInvoice, hashDocumentoAnterior);
        }
        private bool CanTestHash(object arg)
        {
            return this.CurrentInvoice != null;
        }

        public SimpleCommand<object, object> DoOpenExcelCommand { get; private set; }
        private void OpenExcel(object args)
        {
            InvoiceViewModel.OpenInvoiceInExcel(this.CurrentInvoice, this.ShowAllLines ? this.SalesInvoices.Invoice.SelectMany(i => i.Line).ToArray() : this.CurrentInvoiceLines);
        }
        private bool CanOpenExcel(object args)
        {
            if (CurrentInvoice != null)
                return true;
            else
                return false;
        }

        public SimpleCommand<object, object> ShowInvoiceDetailsCommand { get; private set; }
        private void DoShowInvoiceDetails(object args)
        {
            Workspace.Workspace.Instance.ShowInvoiceDetails(this.CurrentInvoice);
        }
        private bool CanShowInvoiceDetails(object args)
        {
            return this.CurrentInvoice != null;
        }

        public SimpleCommand<object, object> ShowCustomerCommand { get; private set; }
        private void DoShowCustomer(object args)
        {
            Workspace.Workspace.Instance.ShowCustomer(this.CurrentInvoice.Pk);
        }
        private bool CanShowCustomer(object args)
        {
            return this.CurrentInvoice != null;
        }
        #endregion

        private int Operation(SourceDocumentsSalesInvoicesInvoice i, SourceDocumentsSalesInvoicesInvoiceLine l)
        {
            return SaftAnalyser.Instance.Operation(i, l);
        }
    }
}
